///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file formic/random/random.cpp
///
/// \brief   implementation for functions and objects associated with random numbers
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#include <formic/random/random.h>
#include <formic/mpi/interface.h>
#include <formic/command/command.h>
#include <formic/output/output.h>

// initialize the global linear congruential random number generator
formic::LinearCongruentialGenerator formic::global_lcg;

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   sets the random seed on each process
///
/// \param[in]     seed     the random seed
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void formic::set_seed(unsigned int seed) {

  // get MPI info
  const int nproc  = formic::mpi::size();
  const int myrank = formic::mpi::rank();

  // report what seed is used
  if (myrank == 0)
    formic::of << boost::format("using a random seed of %i") % seed << std::endl << std::endl;

  // temporarily initialize using the provided seed
  formic::global_lcg.set_seed(seed);

  // produce a different seed for each process
  std::vector<unsigned int> seeds(nproc);
  for (int i = 0; i < nproc; i++) {
    unsigned int temp;
    for (int j = 0; j < 1000000; j++) temp = formic::global_lcg(); // separate the seeds by a million random numbers
    seeds.at(i) = formic::global_lcg();
  }

  // give every process a different seed
  formic::mpi::scatter(&seeds.at(0), &seed, 1, 0);

  // initialize using this process's seed
  formic::global_lcg.set_seed(seed);

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Function called for the "SetSeed" command, which sets the random seed to the given
///          integer, or to the current time if a negative integer is supplied
///
/// \param[in]     arch         archive to read command arguments from
/// \param[in]     data         data on which to execute the command
///
///////////////////////////////////////////////////////////////////////////////////////////////////

static void formic_function_for_command_SetSeed(formic::Archive & arch, formic::AllData & data) {

  // read name of coefficient object whose energy we will compute
  int seed;
  formic::mpi::read_and_bcast(arch, seed, "Failed to read seed in formic_function_for_command_SetSeed.");

  // if requested, use the current time
  if (seed < 0)
    seed = std::time(0);

  // ensure the seed is not zero
  if (formic::mpi::rank() == 0 && seed == 0)
    throw formic::Exception("random seed cannot be zero");

  // set the random seed
  formic::set_seed(seed);

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Add random number related commands to the formic command system
///
///////////////////////////////////////////////////////////////////////////////////////////////////

void formic::init_random_commands() {

  // add command to add an agp pairing matrix to the wavefunction data, derived from RHF coefficients
  formic::add_command("SetSeed", &formic_function_for_command_SetSeed);

}
